﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Meraz.Core.Data;
using Meraz.Core.Cache;
using System.Xml.Serialization;
using System.Web.Caching;
using Meraz.Core.Model;
using System.Diagnostics;

namespace Meraz.Core.Repositories
{
    public abstract class XmlRepositoryBase<T, D> : RepositoryBase<T, string>
        where D : IXmlStore<T>
        where T : IXmlEntity
    {
        public CacheManager<D> CacheManager { get; set; }

        public D Data { get; set; }

        public string FileName { get; protected set; }
        
        public XmlRepositoryBase(string fileName)
        {
            this.FileName = fileName;

            CacheManager = new CacheManager<D>(GetCacheKey(), 900);
        }

        public override List<T> All()
        {
            if (Data == null)
                LoadFromCache();

            return Data.Items.ToList();
        }

        public abstract T Create();

        public override void Delete(T entity)
        {
            if (Data == null)
                LoadFromCache();
            int index = Data.Items.IndexOf(entity);
            if (index > -1)
            {
                Data.Items.Remove(entity);
                Serialize();
            }
        }
        
        public override T GetById(string id)
        {
            if (Data == null)
                LoadFromCache();

            return Data.Items.Where(where1 => where1.ID == id).FirstOrDefault();
        }

        public abstract string GetCacheKey();
   
        protected void LoadFromCache()
        {
            bool isCaching = CacheManager.CheckCache();
            if (isCaching)
            {
                Data = CacheManager.Grab();
            }
            if (Data == null)
            {
                XmlSerializer mySerializer = new XmlSerializer(typeof(D));
                FileStream myFileStream = null;
                try
                {
                    try
                    {
                        myFileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
                    }
                    catch (System.IO.FileNotFoundException)
                    {
                        Debug.WriteLine(string.Format("The file '{0}' was not found.", FileName));
                        return;
                    }
                    Data = (D)mySerializer.Deserialize(myFileStream);
                    if (isCaching)
                    {
                        CacheManager.Insert(Data, CacheItemPriority.Normal);
                    }
                }
                finally
                {
                    if (myFileStream != null)
                    {
                        myFileStream.Close();
                    }
                }
            }
        }

        public void Save(T t)
        {
            if (Data == null)
                LoadFromCache();
            int index = Data.Items.IndexOf(t);
            if (index > -1)
            {
                T target = Data.Items[index];
                Transfer(target, t);
            }
            else
            {
                Data.Items.Add(t);
            }
            Serialize();
            Data = default(D);
            CacheManager.Clear();
        }

        public void Save(IEnumerable<T> items)
        {
            Data.Items = new List<T>();
            foreach(T item in items)
                Data.Items.Add(item);
            Serialize();
            CacheManager.Clear();
        }

        protected void Serialize()
        {
            CacheManager.Clear();
            //FileStream stream = new FileStream(FileName, FileMode.Open, FileAccess.Write);
            using (TextWriter textWriter = new StreamWriter(FileName,
                false, System.Text.Encoding.UTF8))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(D));
                serializer.Serialize(textWriter, Data);
                textWriter.Flush();
            }
            Data = default(D);
        }

        public abstract void Transfer(T target, T source);
    }
}
